package gui;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.Scanner;

import javax.swing.*;
import javax.swing.event.*;

@SuppressWarnings("serial")
public class UndoableTextArea extends JTextArea implements CaretListener {
	private LinkedList<UndoEntry> undoStack, redoStack;
	private ArrayList<UndoableTextAreaListener> listeners;
	private String prevText;
	private int prevPosition;
	private boolean userUpdating, needsSaving;
	
	private class UndoEntry {
		public UndoEntry(String text, int caretPosition) {
			this.text = text;
			this.caretPosition = caretPosition;
		}
		
		String text;
		int caretPosition;
	}
	
	public UndoableTextArea() {
		super();
		undoStack = new LinkedList<UndoEntry>();
		resetRedo();
		prevText = "";
		prevPosition = 0;
		addCaretListener(this);
		userUpdating = true;
		needsSaving = false;
		listeners = new ArrayList<UndoableTextAreaListener>();
	}
	
	public void addUndoableTextAreaListener(UndoableTextAreaListener utal) {
		listeners.add(utal);
	}
	
	private void notifyListeners() {
		for (UndoableTextAreaListener utal: listeners) {
			utal.statusChanged(this);
		}
	}
	
	public boolean needsSaving() {
		return needsSaving;
	}
	
	private void toggleNeedsSaving(boolean needsSaving) {
		this.needsSaving = needsSaving;
		notifyListeners();
	}
	
	public void save(File f) throws IOException {
		PrintWriter p = new PrintWriter(new FileWriter(f));
		p.println(getText());
		p.close();
		toggleNeedsSaving(false);
	}
	
	public void open(File f) throws FileNotFoundException {
		Scanner s = new Scanner(f);
		String textRead = "";
		while (s.hasNextLine()) {
			textRead += s.nextLine() + "\n";
		}
		changeText(textRead, 0);
		toggleNeedsSaving(false);
	}
	
	private void resetUndo() {
		undoStack = new LinkedList<UndoEntry>();
		resetRedo();
	}
	
	private void resetRedo() {
		redoStack = new LinkedList<UndoEntry>();
	}

	public void undo() {
		if (!undoStack.isEmpty()) {
			redoStack.addFirst(makeEntry());
			changeText(undoStack.removeFirst());
			if (undoStack.isEmpty()) {
				toggleNeedsSaving(false); // I think this is right...
			}
		}
	}
	
	private UndoEntry makeEntry() {
		return new UndoEntry(getText(), getCaretPosition());
	}

	public void redo() {
		if (!redoStack.isEmpty()) {
			undoStack.addFirst(makeEntry());
			changeText(redoStack.removeFirst());
		}
	}
	
	private void changeText(UndoEntry e) {
		changeText(e.text, e.caretPosition);
	}
	
	private void changeText(String text, int where) {
		userUpdating = false;
		setText(text);
		setCaretPosition(where);
		userUpdating = true;
		toggleNeedsSaving(true);
	}

	public void caretUpdate(CaretEvent e) {
		if (userUpdating && !prevText.equals(getText())) {
			undoStack.addFirst(new UndoEntry(prevText, prevPosition));
			resetRedo();
			toggleNeedsSaving(true);
		} 
		prevText = getText();
		prevPosition = getCaretPosition();
	}
}
